#include "wxwidgetsgui.h"
#include "wxwidgetsguiconfigpanel.h"
#include "wxwidgetsguiappadoptingdlg.h"
#include "wxwidgetsres.h"
#include "../wxscoder.h"
#include "../wxsproject.h"

IMPLEMENT_CLASS( wxWidgetsGUI, wxsGUI )

wxWidgetsGUI::wxWidgetsGUI( wxsProject* Project ):
  wxsGUI( _T( "wxWidgets" ), Project ),
  m_AppFile(),
  m_LoadedResources(),
  m_MainResource(),
  m_CallInitAll( true ),
  m_CallInitAllNecessary( true ),
  m_AppLanguage( wxsCPP ) {
}

wxWidgetsGUI::~wxWidgetsGUI() {
}

void wxWidgetsGUI::SetAppSourceFile( const wxString& NewAppFile ) {
  m_AppFile = NewAppFile;
  m_AppLanguage = wxsCodeMarks::IdFromExt( wxFileName( NewAppFile ).GetExt() );
}

cbConfigurationPanel* wxWidgetsGUI::OnBuildConfigurationPanel( wxWindow* Parent ) {
  return new wxWidgetsGUIConfigPanel( Parent, this );
}

void wxWidgetsGUI::OnRebuildApplicationCode() {
  bool IsAnyXRC = false;
  wxWidgetsRes* MainResPtr = 0;
  size_t Count = GetProject()->GetResourcesCount();
  for( size_t i = 0; i < Count; i++ ) {
    wxWidgetsRes* Res = wxDynamicCast( GetProject()->GetResource( i ), wxWidgetsRes );
    if( !Res ) {
      continue;
    }
    if( Res->GetGUI() != GetName() ) {
      continue;
    }
    if( m_MainResource == Res->GetResourceName() && Res->GetLanguage() == m_AppLanguage ) {
      MainResPtr = Res;
    }
    if( Res->OnGetUsingXRC() ) {
      IsAnyXRC = true;
    }
  }
  wxString NewCode;
  switch( m_AppLanguage ) {
    case wxsCPP:
      NewCode = _T( "\nbool wxsOK = true;\n" )
                _T( "wxInitAllImageHandlers();\n" );
      break;
    case wxsUnknownLanguage:
    default:
      break;
  }
  bool InitAllXRCHandlers = m_CallInitAll && ( IsAnyXRC || !m_CallInitAllNecessary );
  if( InitAllXRCHandlers ) {
    switch( m_AppLanguage ) {
      case wxsCPP:
        NewCode.Append( _T( "wxXmlResource::Get()->InitAllHandlers();\n" ) );
        break;
      case wxsUnknownLanguage:
      default:
        break;
    }
  }
  for( size_t i = 0; i < m_LoadedResources.Count(); ++i ) {
    switch( m_AppLanguage ) {
      case wxsCPP:
        NewCode.Append( _T( "wxsOK = wxsOK && wxXmlResource::Get()->Load(_T(\"" ) );
        NewCode.Append( m_LoadedResources[i] );
        NewCode.Append( _T( "\"));\n" ) );
        break;
      case wxsUnknownLanguage:
      default:
        break;
    }
  }
  if( MainResPtr ) {
    switch( m_AppLanguage ) {
      case wxsCPP:
        NewCode << _T( "if ( wxsOK )\n{\n" );
        NewCode << MainResPtr->GetAppBuildingCode();
        NewCode << _T( "}\n" );
        break;
      case wxsUnknownLanguage:
      default:
        break;
    }
  }
  wxsCoder::Get()->AddCode(
    GetProjectPath() + m_AppFile,
    wxsCodeMarks::Beg( m_AppLanguage, _T( "AppInitialize" ) ),
    wxsCodeMarks::End( m_AppLanguage ),
    NewCode );
  NewCode = _T( "\n" );
  if( MainResPtr ) {
    switch( m_AppLanguage ) {
      case wxsCPP: {
        wxString IncludeFile = MainResPtr->GetDeclarationFile();
        wxFileName IncludeFileName( GetProjectPath() + IncludeFile );
        if( IncludeFileName.MakeRelativeTo( GetProjectPath() ) ) {
          IncludeFile = IncludeFileName.GetFullPath( wxPATH_UNIX );
        }
        NewCode << _T( "#include \"" ) << IncludeFile << _T( "\"\n" );
        break;
      }
      case wxsUnknownLanguage:
      default:
        break;
    }
  }
  if( InitAllXRCHandlers || m_LoadedResources.Count() ) {
    switch( m_AppLanguage ) {
      case wxsCPP:
        NewCode.Append( _T( "#include <wx/xrc/xmlres.h>\n" ) );
        break;
      case wxsUnknownLanguage:
      default:
        break;
    }
  }
  switch( m_AppLanguage ) {
    case wxsCPP:
      NewCode.Append( _T( "#include <wx/image.h>\n" ) );
      break;
    case wxsUnknownLanguage:
    default:
      break;
  }
  wxsCoder::Get()->AddCode(
    GetProjectPath() + m_AppFile,
    wxsCodeMarks::Beg( m_AppLanguage, _T( "AppHeaders" ) ),
    wxsCodeMarks::End( m_AppLanguage ),
    NewCode );
}

bool wxWidgetsGUI::OnCheckIfApplicationManaged() {
  return IsAppSourceManaged( m_AppFile, m_AppLanguage );
}

bool wxWidgetsGUI::OnCreateApplicationBinding() {
  wxWidgetsGUIAppAdoptingDlg Dlg( 0, this );
  Dlg.ShowModal();
  return OnCheckIfApplicationManaged();
}

void wxWidgetsGUI::OnReadConfig( TiXmlElement* element ) {
  m_AppFile      = cbC2U( element->Attribute( "src" ) );
  m_MainResource = cbC2U( element->Attribute( "main" ) );
  wxString InAll = cbC2U( element->Attribute( "init_handlers" ) );
  if( InAll == _T( "never" ) ) {
    m_CallInitAll = false;
    m_CallInitAllNecessary = false;
  } else if( InAll == _T( "always" ) ) {
    m_CallInitAll = true;
    m_CallInitAllNecessary = false;
  } else {
    m_CallInitAll = true;
    m_CallInitAllNecessary = true;
  }
  wxString Lang  = cbC2U( element->Attribute( "language" ) );
  m_AppLanguage = wxsCodeMarks::Id( Lang );
  TiXmlElement* LoadRes = element->FirstChildElement( "load_resource" );
  while( LoadRes ) {
    wxString FileName = cbC2U( LoadRes->Attribute( "file" ) );
    if( !FileName.empty() ) {
      m_LoadedResources.Add( FileName );
    }
    LoadRes = LoadRes->NextSiblingElement( "load_resource" );
  }
}

void wxWidgetsGUI::OnWriteConfig( TiXmlElement* element ) {
  element->SetAttribute( "src", cbU2C( m_AppFile ) );
  element->SetAttribute( "main", cbU2C( m_MainResource ) );
  if( m_CallInitAll && m_CallInitAllNecessary ) {
    element->SetAttribute( "init_handlers", "necessary" );
  } else if( m_CallInitAll ) {
    element->SetAttribute( "init_handlers", "always" );
  } else {
    element->SetAttribute( "init_handlers", "never" );
  }
  element->SetAttribute( "language", cbU2C( wxsCodeMarks::Name( m_AppLanguage ) ) );
  for( size_t i = 0; i < m_LoadedResources.GetCount(); ++i ) {
    TiXmlElement* LoadRes = element->InsertEndChild( TiXmlElement( "load_resource" ) )->ToElement();
    LoadRes->SetAttribute( "file", cbU2C( m_LoadedResources[i] ) );
  }
}

bool wxWidgetsGUI::IsAppSourceManaged( const wxString& FileName, wxsCodingLang Lang ) {
  if( FileName.empty() ) {
    return false;
  }
  if( wxsCoder::Get()->GetCode(
        GetProjectPath() + FileName,
        wxsCodeMarks::Beg( Lang, _T( "AppInitialize" ) ),
        wxsCodeMarks::End( Lang )
      ).empty() ) {
    return false;
  }
  if( wxsCoder::Get()->GetCode(
        GetProjectPath() + FileName,
        wxsCodeMarks::Beg( Lang, _T( "AppHeaders" ) ),
        wxsCodeMarks::End( Lang )
      ).empty() ) {
    return false;
  }
  return true;
}

bool wxWidgetsGUI::ScanForApp( ProjectFile* File ) {
  wxsCodingLang Lang = wxsCodeMarks::IdFromExt( File->file.GetExt() );
  if( Lang == wxsUnknownLanguage ) {
    return false;
  }
  if( IsAppSourceManaged( File->relativeFilename, Lang ) ) {
    return true;
  }
  wxFontEncoding Encoding;
  bool UseBOM;
  wxString Source = wxsCoder::Get()->GetFullCode( File->file.GetFullPath(), Encoding, UseBOM );
  if( GetAppClassName( Source, Lang ).empty() ) {
    return false;
  }
  switch( Lang ) {
    case wxsCPP: {
      int Pos = Source.Find( _T( "OnInit" ) );
      if( Pos < 0 ) {
        return false;
      }
      return true;
    }
    case wxsUnknownLanguage:
    default:
      break;
  }
  return false;
}

bool wxWidgetsGUI::AddSmithToApp( const wxString& RelativeFileName, wxsCodingLang Lang ) {
  if( !IsAppSourceManaged( RelativeFileName, Lang ) ) {
    wxString FullPath = GetProjectPath() + RelativeFileName;
    wxFontEncoding Encoding;
    bool UseBOM;
    wxString Source = wxsCoder::Get()->GetFullCode( FullPath, Encoding, UseBOM );
    if( Source.empty() ) {
      return false;
    }
    switch( Lang ) {
      case wxsCPP: {
        int Pos = Source.Find( _T( "IMPLEMENT_APP" ) );
        while( Pos > 0 && Source[Pos] != _T( '\n' ) ) {
          Pos--;
        }
        if( Pos > 0 ) {
          Pos++;
        }
        Source.insert( Pos, wxsCodeMarks::Beg( wxsCPP, _T( "AppHeaders" ) ) + _T( "\n" ) +
                       wxsCodeMarks::End( wxsCPP ) + _T( "\n\n" ) );
        wxString ClassName = GetAppClassName( Source, wxsCPP );
        if( ClassName.empty() ) {
          return false;
        }
        wxString SourceCpy = Source;
        Pos = 0;
        while( !SourceCpy.empty() ) {
          int ClassPos = SourceCpy.Find( ClassName );
          if( ClassPos < 0 ) {
            return false;
          }
          ClassPos += ClassName.Length();
          Pos += ClassPos;
          SourceCpy.Remove( 0, ClassPos );
          int MemberPos = 0;
          while( IsWhite( SourceCpy, MemberPos ) ) {
            MemberPos++;
          }
          if( MemberPos + 1 >= ( int )( SourceCpy.Length() ) ) {
            continue;
          }
          if( SourceCpy[MemberPos] != _T( ':' ) && SourceCpy[MemberPos + 1] != _T( ':' ) ) {
            continue;
          }
          MemberPos += 2;
          while( IsWhite( SourceCpy, MemberPos ) ) {
            MemberPos++;
          }
          if( SourceCpy.Mid( MemberPos, 6 ) != _T( "OnInit" ) ) {
            continue;
          }
          MemberPos += 6;
          while( IsWhite( SourceCpy, MemberPos ) ) {
            MemberPos++;
          }
          if( !Match( SourceCpy, MemberPos, _T( '(' ) ) ) {
            continue;
          }
          MemberPos++;
          while( IsWhite( SourceCpy, MemberPos ) ) {
            MemberPos++;
          }
          if( !Match( SourceCpy, MemberPos, _T( ')' ) ) ) {
            continue;
          }
          MemberPos++;
          while( IsWhite( SourceCpy, MemberPos ) ) {
            MemberPos++;
          }
          if( !Match( SourceCpy, MemberPos, _T( '{' ) ) ) {
            continue;
          }
          MemberPos++;
          Pos += MemberPos;
          break;
        }
        if( SourceCpy.empty() ) {
          return false;
        }
        int IndentPos = Pos;
        while( IndentPos > 0 && Source[IndentPos - 1] != _T( '\n' ) && Source[IndentPos - 1] != '\r' ) {
          IndentPos--;
        }
        wxString Indent;
        while( IndentPos < Pos && ( Source[IndentPos] == _T( ' ' ) || Source[IndentPos] == _T( '\t' ) ) ) {
          Indent += Source[IndentPos++];
        }
        Indent.Append( _T( "\t" ) );
        Source = Source( 0, Pos ) +
                 _T( "\n" ) +
                 Indent + wxsCodeMarks::Beg( wxsCPP, _T( "AppInitialize" ) ) + _T( "\n" ) +
                 Indent + wxsCodeMarks::End( wxsCPP ) + _T( "\n" ) +
                 Indent + _T( "return wxsOK;\n" ) + Indent + Source.Mid( Pos );
        wxsCoder::Get()->PutFullCode( FullPath, Source, Encoding, UseBOM );
        break;
      }
      case wxsUnknownLanguage:
      default:
        break;
    }
  }
  m_AppFile = RelativeFileName;
  m_LoadedResources.Clear();
  m_MainResource.Clear();
  m_CallInitAll = true;
  m_CallInitAllNecessary = true;
  m_AppLanguage = Lang;
  OnRebuildApplicationCode();
  NotifyChange();
  return true;
}

wxString wxWidgetsGUI::GetAppClassName( const wxString& Source, wxsCodingLang Lang ) {
  switch( Lang ) {
    case wxsCPP: {
      int Pos = Source.Find( _T( "IMPLEMENT_APP" ) );
      if( Pos < 0 ) {
        return wxEmptyString;
      }
      Pos += 13;
      while( IsWhite( Source, Pos ) ) {
        Pos++;
      }
      if( Pos >= ( int )Source.Length() ) {
        return wxEmptyString;
      }
      if( Source[Pos++] != _T( '(' ) ) {
        return wxEmptyString;
      }
      while( IsWhite( Source, Pos ) ) {
        Pos++;
      }
      wxString ClassName;
      static const wxString AllowedChars( _T( "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_" ) );
      while( ( Pos < ( int )Source.Length() ) && ( AllowedChars.Find( Source[Pos] ) >= 0 ) ) {
        ClassName += Source[Pos];
        Pos++;
      }
      while( IsWhite( Source, Pos ) ) {
        Pos++;
      }
      if( Pos >= ( int )Source.Length() ) {
        return wxEmptyString;
      }
      if( Source[Pos] != _T( ')' ) ) {
        return wxEmptyString;
      }
      return ClassName;
    }
    case wxsUnknownLanguage:
    default:
      break;
  }
  return wxEmptyString;
}

bool wxWidgetsGUI::CreateNewApp( const wxString& FileName ) {
  wxsCodingLang Lang = wxsCodeMarks::IdFromExt( wxFileName( FileName ).GetExt() );
  if( Lang == wxsUnknownLanguage ) {
    return false;
  }
  wxFile Fl( FileName, wxFile::write );
  if( !Fl.IsOpened() ) {
    wxMessageBox( _T( "无法覆盖文件" ) );
    return false;
  }
  switch( Lang ) {
    case wxsCPP: {
      Fl.Write(
        _T( "#include <wx/wxprec.h>\n" )
        _T( "\n" )
        _T( "//(*AppHeaders\n" )
        _T( "//*)\n" )
        _T( "\n" )
        _T( "\n" )
        _T( "class MyApp : public wxApp\n" )
        _T( "{\n" )
        _T( "    public:\n" )
        _T( "        virtual bool OnInit();\n" )
        _T( "};\n" )
        _T( "\n" )
        _T( "IMPLEMENT_APP(MyApp);\n" )
        _T( "\n" )
        _T( "bool MyApp::OnInit()\n" )
        _T( "{\n" )
        _T( "    //(*AppInitialize\n" )
        _T( "    //*)\n" )
        _T( "    return wxsOK;\n" )
        _T( "}\n" )
        _T( "\n" ) );
      break;
    }
    case wxsUnknownLanguage:
    default:
      break;
  }
  wxFileName FN( FileName );
  FN.MakeRelativeTo( GetProjectPath() );
  m_AppFile = FN.GetFullPath();
  m_LoadedResources.Clear();
  m_MainResource.Clear();
  m_CallInitAll = true;
  m_CallInitAllNecessary = true;
  m_AppLanguage = Lang;
  OnRebuildApplicationCode();
  return true;
}

void wxWidgetsGUI::EnumerateMainResources( wxArrayString& Names ) {
  int Count = GetProject()->GetResourcesCount();
  for( int i = 0; i < Count; i++ ) {
    wxWidgetsRes* Res = wxDynamicCast( GetProject()->GetResource( i ), wxWidgetsRes );
    if( Res == 0 ) {
      continue;
    }
    if( Res->GetGUI() != GetName() ) {
      continue;
    }
    if( !Res->OnGetCanBeMain() ) {
      continue;
    }
    if( Res->GetLanguage() != m_AppLanguage ) {
      continue;
    }
    Names.Add( Res->GetResourceName() );
  }
}

inline bool wxWidgetsGUI::IsWhite( wxChar Ch ) {
  return ( Ch == _T( ' ' ) ) || ( Ch == _T( '\n' ) ) || ( Ch == _T( '\r' ) ) || ( Ch == _T( '\t' ) );
}

inline bool wxWidgetsGUI::IsWhite( const wxString& Str, int Pos ) {
  if( Pos >= ( int )Str.Length() ) {
    return false;
  }
  return IsWhite( Str[Pos] );
}

inline bool wxWidgetsGUI::Match( const wxString& Str, int Pos, wxChar Ch ) {
  if( Pos >= ( int )Str.Length() ) {
    return false;
  }
  return Str[Pos] == Ch;
}

static wxsRegisterGUI<wxWidgetsGUI> Reg( _T( "wxWidgets" ) );
